/*
 * Copyright 2012 Gayan Perera
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package gap.maven.script.components;

import gap.maven.script.components.api.ExtractorPattern;
import gap.maven.script.components.spi.FileExtractor;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.maven.plugin.logging.Log;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.io.InputStreamFacade;

/**
 * Default implementation of the {@link FileExtractor} which is based on the
 * java {@link ZipFile}.
 * 
 * @author gayan
 */
public class DefaultFileExtractor implements FileExtractor {
    private static final ExtractorPattern[] EMPTY_PATTERN = new ExtractorPattern[0];
    private Log logger;

    public DefaultFileExtractor(Log logger) {
        this.logger = logger;
    }

    public void extract(File archive, File toDir, ExtractorPattern[] patterns) throws IOException {
        if (logger.isDebugEnabled()) {
            logger.debug("Extracting file : " + archive.getAbsolutePath());
        }

        final ZipFile archiveZFile = new ZipFile(archive);
        final Enumeration<? extends ZipEntry> entries = archiveZFile.entries();

        try {
            final InputStreamFacadeAdapter streamAdapter = new InputStreamFacadeAdapter();

            while (entries.hasMoreElements()) {
                final ZipEntry entry = entries.nextElement();
                if (!entry.isDirectory()) {
                    // if no patterns given we will extract the current entry.
                    boolean extractFile = false;
                    if (patterns.length > 0) {
                        for (ExtractorPattern pattern : patterns) {
                            if (pattern.match(entry)) {
                                extractFile = true;
                                break;
                            }
                        }
                    } else {
                        extractFile = true;
                    }

                    if (extractFile) {
                        FileUtils.copyStreamToFile(streamAdapter.adapt(archiveZFile.getInputStream(entry)),
                                toDestFile(toDir, entry));

                        if (logger.isDebugEnabled()) {
                            logger.debug("Archive content : " + entry.getName() + " extracted.");
                        }
                    }
                }
            }
        } finally {
            archiveZFile.close();
        }

        if (logger.isDebugEnabled()) {
            logger.debug("File extracted to : " + toDir.getAbsolutePath());
        }
    }

    public void extract(File archive, File toDir) throws IOException {
        extract(archive, toDir, EMPTY_PATTERN);
    }

    private File toDestFile(File toDir, ZipEntry entry) {
        File file = new File(toDir, entry.getName());
        FileUtils.mkdir(FileUtils.dirname(file.getPath()));
        return file;
    }

    private class InputStreamFacadeAdapter implements InputStreamFacade {
        private InputStream adaptee = null;

        public InputStreamFacade adapt(InputStream stream) {
            if (stream == null) {
                throw new IllegalArgumentException("stream cannot be null.");
            }
            this.adaptee = stream;
            return this;
        }

        public InputStream getInputStream() throws IOException {
            return adaptee;
        }
    }
}
